module SFI_LOOP_CONTROL (
   RESET,
   SFI4_RCLK,
   SFI4_RDATA,
   SFI4_TCLK,
   SFI4_TDATA,
   CORE_RCLK,
   CORE_RDATA,
   CORE_TCLK,
   CORE_TDATA,

	chnn_tx_pclk_p,
	chnn_tx_pclk_n,
   
   MPI_SFI2SFI_LOOP,     // loop SFI4 Received data to transmit data
   MPI_CORE2CORE_LOOP,   // loop core logic transmit data to received data
   MPI_FIFO_CLEAR,
   MPI_ERROR_CLEAR,
   MPI_FIFO_FULL,
   MPI_FIFO_EMPTY
   );

input              RESET;
input              SFI4_RCLK;
input[63:0]        SFI4_RDATA;
input              SFI4_TCLK;
output reg[63:0]   SFI4_TDATA;
input              CORE_RCLK;
output reg[63:0]   CORE_RDATA;
input              CORE_TCLK;
input[63:0]        CORE_TDATA;

input				chnn_tx_pclk_p;
input				chnn_tx_pclk_n;

input              MPI_SFI2SFI_LOOP;
input              MPI_CORE2CORE_LOOP;
input              MPI_FIFO_CLEAR;
input              MPI_ERROR_CLEAR;
output[3:0]        MPI_FIFO_FULL;
output[3:0]        MPI_FIFO_EMPTY;

reg[7:0]           SFIRX_FIFO_CLEAR, CORETX_FIFO_CLEAR;   // FIFO clear signal after clock domain change
reg[7:0]           SFIRX_FIFO_STARTCNT, CORETX_FIFO_STARTCNT;
reg                SFIRX_FIFO_RESET, CORETX_FIFO_RESET;
reg                SFIRX_FIFO_WR_EN, CORETX_FIFO_WR_EN;
reg                SFIRX_FIFO_RD_EN, CORETX_FIFO_RD_EN;

reg                SFITX_ERROR_CLEAR;
reg                SFITX_SFI2SFI_LOOP, SFITX_SFI2SFI_RD_EN, SFITX_CORE2SFI_RD_EN;
wire[63:0]         SFITX_SFI2SFI_DATA, SFTTX_CORE2SFI_DATA;
wire               SFITX_SFI2SFI_FULL, SFITX_CORE2SFI_FULL, SFITX_SFI2SFI_EMPTY, SFITX_CORE2SFI_EMPTY;
reg                MPI_SFI2SFI_FULL,   MPI_CORE2SFI_FULL,   MPI_SFI2SFI_EMPTY,   MPI_CORE2SFI_EMPTY;

reg                CORERX_ERROR_CLEAR;
reg                CORERX_CORE2CORE_LOOP, CORERX_CORE2CORE_RD_EN, CORERX_SFI2CORE_RD_EN;
wire[63:0]         CORERX_CORE2CORE_DATA, CORERX_SFI2CORE_DATA;
wire               CORERX_CORE2CORE_FULL, CORERX_SFI2CORE_FULL, CORERX_ORE2CORE_EMPTY, CORERX_SFI2CORE_EMPTY;
reg                MPI_CORE2CORE_FULL,    MPI_SFI2CORE_FULL,    MPI_CORE2CORE_EMPTY,    MPI_SFI2CORE_EMPTY;
reg	[63:0]			pclk_cnt;

//****** Write SFI Rx/CORE Tx data into buffer, data write into dual buffers, for normal data path and loop back data path  ******//

// SFI data write control and FIFO instance
always @(posedge RESET or posedge SFI4_RCLK) begin
   if ( RESET==1'b1 )
      SFIRX_FIFO_CLEAR              <= 1'b0;
   else
      SFIRX_FIFO_CLEAR              <= MPI_FIFO_CLEAR;
end
always @(posedge RESET or posedge SFI4_RCLK) begin
   if ( RESET==1'b1 )
      SFIRX_FIFO_STARTCNT[7:0]       <= 7'd0;
   else begin
      if ( SFIRX_FIFO_CLEAR==1'b1 )
         SFIRX_FIFO_STARTCNT[7:0]    <= 7'd0;
      else if ( SFIRX_FIFO_STARTCNT[7:0]!=8'hff )
         SFIRX_FIFO_STARTCNT[7:0]    <= SFIRX_FIFO_STARTCNT[7:0] +8'd1;
   end
end
always @(posedge RESET or posedge SFI4_RCLK) begin
   if ( RESET==1'b1 )
      SFIRX_FIFO_RESET                <= 1'b1;
   else begin
      if ( SFIRX_FIFO_STARTCNT[7:0]==8'd0 )
         SFIRX_FIFO_RESET             <= 1'b1;
      else
         SFIRX_FIFO_RESET             <= 1'b0;
   end
end
always @(posedge RESET or posedge SFI4_RCLK) begin
   if ( RESET==1'b1 )
      SFIRX_FIFO_WR_EN                <= 1'b0;
   else begin
      if ( SFIRX_FIFO_STARTCNT[7:0]==8'd0 )
         SFIRX_FIFO_WR_EN             <= 1'b0;
      else if ( SFIRX_FIFO_STARTCNT[7:0]==8'h7F )
         SFIRX_FIFO_WR_EN             <= 1'b1;
   end
end
always @(posedge RESET or posedge SFI4_RCLK) begin
   if ( RESET==1'b1 )
      SFIRX_FIFO_RD_EN                <= 1'b0;
   else begin
      if ( SFIRX_FIFO_STARTCNT[7:0]==8'd0 )
         SFIRX_FIFO_RD_EN             <= 1'b0;
      else if ( SFIRX_FIFO_STARTCNT[7:0]==8'hBF )  // enbale the read control after 64 clock cycle write
         SFIRX_FIFO_RD_EN             <= 1'b1;
   end
end

/*WRAP_FIFO8K_64_64   INST_FIFO_SFI2SFI(   // data path SFI-SFI loopback data path buffer
   .RESET         ( SFIRX_FIFO_RESET ),
   .WR_CLK        ( SFI4_RCLK ),
   .RD_CLK        ( SFI4_TCLK ),
   .DIN           ( SFI4_RDATA[63:0] ),
   .WR_EN         ( SFIRX_FIFO_WR_EN ) ,
   .RD_EN         ( SFITX_SFI2SFI_RD_EN ),
   .DOUT          ( SFITX_SFI2SFI_DATA[63:0] ),
   .FULL          ( SFITX_SFI2SFI_FULL ),
   .EMPTY         ( SFITX_SFI2SFI_EMPTY )
   );*/
WRAP_FIFO8K_64_64   INST_FIFO_SFI2CORE( // data path SFI-CORE normal data path buffer
   .RESET         ( SFIRX_FIFO_RESET ),
   .WR_CLK        ( SFI4_RCLK ),
   .RD_CLK        ( CORE_RCLK ),
   .DIN           ( SFI4_RDATA[63:0] ),
   .WR_EN         ( SFIRX_FIFO_WR_EN ) ,
   .RD_EN         ( CORERX_SFI2CORE_RD_EN ),
   .DOUT          ( CORERX_SFI2CORE_DATA[63:0] ),
   .FULL          ( CORERX_SFI2CORE_FULL ),
   .EMPTY         ( CORERX_SFI2CORE_EMPTY )
   );


// CORE data write control and FIFO instance
always @(posedge RESET or posedge CORE_TCLK) begin
   if ( RESET==1'b1 )
      CORETX_FIFO_CLEAR               <= 1'b0;
   else
      CORETX_FIFO_CLEAR               <= MPI_FIFO_CLEAR;
end
always @(posedge RESET or posedge CORE_TCLK) begin
   if ( RESET==1'b1 )
      CORETX_FIFO_STARTCNT[7:0]       <= 7'd0;
   else begin
      if ( CORETX_FIFO_CLEAR==1'b1 )
         CORETX_FIFO_STARTCNT[7:0]    <= 7'd0;
      else if ( CORETX_FIFO_STARTCNT[7:0]!=8'hff )
         CORETX_FIFO_STARTCNT[7:0]    <= CORETX_FIFO_STARTCNT[7:0] +8'd1;
   end
end
always @(posedge RESET or posedge CORE_TCLK) begin
   if ( RESET==1'b1 )
      CORETX_FIFO_RESET               <= 1'b1;
   else begin
      if ( CORETX_FIFO_STARTCNT[7:0]==8'd0 )
         CORETX_FIFO_RESET            <= 1'b1;
      else
         CORETX_FIFO_RESET            <= 1'b0;
   end
end
always @(posedge RESET or posedge CORE_TCLK) begin
   if ( RESET==1'b1 )
      CORETX_FIFO_WR_EN               <= 1'b0;
   else begin
      if ( CORETX_FIFO_STARTCNT[7:0]==8'd0)
         CORETX_FIFO_WR_EN            <= 1'b0;
      else if ( CORETX_FIFO_STARTCNT[7:0]==8'h7F )
         CORETX_FIFO_WR_EN            <= 1'b1;
   end
end
always @(posedge RESET or posedge CORE_TCLK) begin
   if ( RESET==1'b1 )
      CORETX_FIFO_RD_EN               <= 1'b0;
   else begin
      if ( CORETX_FIFO_STARTCNT[7:0]==8'd0)
         CORETX_FIFO_RD_EN            <= 1'b0;
      else if ( CORETX_FIFO_STARTCNT[7:0]==8'hBF )  // enbale the read control after 64 clock cycle write
         CORETX_FIFO_RD_EN            <= 1'b1;
   end
end

WRAP_FIFO8K_64_64   INST_FIFO_CORE2SFI(  // data path CORE-SFI normal data path buffer
   .RESET         ( CORETX_FIFO_RESET ),
   .WR_CLK        ( CORE_TCLK ),
   .RD_CLK        ( SFI4_TCLK ),
   .DIN           ( CORE_TDATA[63:0] ),
   .WR_EN         ( CORETX_FIFO_WR_EN ) ,
   .RD_EN         ( SFITX_CORE2SFI_RD_EN ),
   .DOUT          ( SFTTX_CORE2SFI_DATA[63:0] ),
   .FULL          ( SFITX_CORE2SFI_FULL ),
   .EMPTY         ( SFITX_CORE2SFI_EMPTY )
   );
/*WRAP_FIFO8K_64_64   INST_FIFO_CORE2CORE( // data path CORE-CORE loop data path buffer
   .RESET         ( CORETX_FIFO_RESET ),
   .WR_CLK        ( CORE_TCLK ),
   .RD_CLK        ( CORE_RCLK ),
   .DIN           ( CORE_TDATA[63:0] ),
   .WR_EN         ( CORETX_FIFO_WR_EN ) ,
   .RD_EN         ( CORERX_CORE2CORE_RD_EN ),
   .DOUT          ( CORERX_CORE2CORE_DATA[63:0] ),
   .FULL          ( CORERX_CORE2CORE_FULL ),
   .EMPTY         ( CORERX_ORE2CORE_EMPTY )
   );
*/



//****** SFI Tx DATA read control, control by the MPI_SFI2SFI_LOOP, enable the SFI2SFI LOOP when MPI_SFI2SFI_LOOP set to '1' ******
//IBUFDS #(
//	.DIFF_TERM("TRUE"), 
//	.IOSTANDARD("LVDS_25"))
//	PCLK_IN(
//		.O(pclk), 
//		.I(chnn_tx_pclk_p), 
//		.IB(chnn_tx_pclk_n)
//		);

always @(posedge RESET or posedge SFI4_TCLK) 
begin
	if ( RESET==1'b1 ) 
	begin
		pclk_cnt[63:0] <= 0;
	end
	else
	begin
		pclk_cnt[63:0] <= pclk_cnt[63:0] + 1;
	end	
end


// change clock domian of MPI_SFI2SFI_LOOP/SFIRX_FIFO_RD_EN/CORETX_FIFO_RD_EN/MPI_ERROR_CLEAR to SFI4_TCLK,
always @(posedge RESET or posedge SFI4_TCLK) begin
   if ( RESET==1'b1 ) begin
      SFITX_SFI2SFI_LOOP              <= 1'b0;
      SFITX_SFI2SFI_RD_EN             <= 1'b0;
      SFITX_CORE2SFI_RD_EN            <= 1'b0;
      SFITX_ERROR_CLEAR               <= 1'b0;
   end
   else begin
      SFITX_SFI2SFI_LOOP              <= MPI_SFI2SFI_LOOP;
      SFITX_SFI2SFI_RD_EN             <= SFIRX_FIFO_RD_EN;
      SFITX_CORE2SFI_RD_EN            <= CORETX_FIFO_RD_EN;
      SFITX_ERROR_CLEAR               <= MPI_ERROR_CLEAR;
   end
end

// select output data of SFI Tx data
always @(posedge RESET or posedge SFI4_TCLK) begin
   if ( RESET==1'b1 )
      SFI4_TDATA[63:0]                <= 64'd0;
   else begin
      if ( SFITX_SFI2SFI_LOOP==1'b1 )
         SFI4_TDATA[63:0]             <= SFITX_SFI2SFI_DATA[63:0];            // for ddvtest
//		begin
//			if((SFITX_SFI2SFI_DATA[7:4]+SFITX_SFI2SFI_DATA[3:0]) == 4'hf)
//			begin
//				SFI4_TDATA[63:0] 	<= SFITX_SFI2SFI_DATA[63:0];
//			end
//			else
//			begin
//				SFI4_TDATA[63:0]             <= pclk_cnt[63:0];
//			end
//		end
      else
         SFI4_TDATA[63:0]             <= SFTTX_CORE2SFI_DATA[63:0];
   end
end

// generate SFI Tx FIFO alarm, 
always @(posedge RESET or posedge SFI4_TCLK) begin
   if ( RESET==1'b1 ) begin
      MPI_SFI2SFI_FULL                <= 1'b0;
      MPI_CORE2SFI_FULL               <= 1'b0;
      MPI_SFI2SFI_EMPTY               <= 1'b0;
      MPI_CORE2SFI_EMPTY              <= 1'b0;
   end
   else begin
      if ( SFITX_ERROR_CLEAR==1'b1 )  begin
         MPI_SFI2SFI_FULL             <= 1'b0;
         MPI_CORE2SFI_FULL            <= 1'b0;
         MPI_SFI2SFI_EMPTY            <= 1'b0;
         MPI_CORE2SFI_EMPTY           <= 1'b0;
      end
      else begin
         MPI_SFI2SFI_FULL             <= MPI_SFI2SFI_FULL   | SFITX_SFI2SFI_FULL;
         MPI_CORE2SFI_FULL            <= MPI_CORE2SFI_FULL  | SFITX_CORE2SFI_FULL;
         MPI_SFI2SFI_EMPTY            <= MPI_SFI2SFI_EMPTY  | SFITX_SFI2SFI_EMPTY;
         MPI_CORE2SFI_EMPTY           <= MPI_CORE2SFI_EMPTY | SFITX_CORE2SFI_EMPTY;
      end
   end
end



//****** CORE Rx DATA read control, control by the MPI_CORE2CORE_LOOP, enable the CORE2CORE LOOP when MPI_CORE2CORE_LOOP set to '1' ******//

// change clock domian of MPI_CORE2CORE_LOOP/SFIRX_FIFO_RD_EN/CORETX_FIFO_RD_EN/MPI_ERROR_CLEAR to CORE_RCLK,
always @(posedge RESET or posedge CORE_RCLK) begin
   if ( RESET==1'b1 ) begin
      CORERX_CORE2CORE_LOOP           <= 1'b0;
      CORERX_SFI2CORE_RD_EN           <= 1'b0;
      CORERX_CORE2CORE_RD_EN          <= 1'b0;
      CORERX_ERROR_CLEAR              <= 1'b0;
   end
   else begin
      CORERX_CORE2CORE_LOOP           <= MPI_CORE2CORE_LOOP;
      CORERX_SFI2CORE_RD_EN           <= SFIRX_FIFO_RD_EN;
      CORERX_CORE2CORE_RD_EN          <= CORETX_FIFO_RD_EN;
      CORERX_ERROR_CLEAR              <= MPI_ERROR_CLEAR;
   end
end

// select output data of CORE Rx data
always @(posedge RESET or posedge CORE_RCLK) begin
   if ( RESET==1'b1 )
      CORE_RDATA[63:0]                <= 64'd0;
   else begin
      if ( CORERX_CORE2CORE_LOOP==1'b1 )
         CORE_RDATA[63:0]             <= CORERX_CORE2CORE_DATA[63:0];
      else
         CORE_RDATA[63:0]             <= CORERX_SFI2CORE_DATA[63:0];
   end
end

// generate SFI Tx FIFO alarm, 
always @(posedge RESET or posedge CORE_RCLK) begin
   if ( RESET==1'b1 ) begin
      MPI_CORE2CORE_FULL              <= 1'b0;
      MPI_SFI2CORE_FULL               <= 1'b0;
      MPI_CORE2CORE_EMPTY             <= 1'b0;
      MPI_SFI2CORE_EMPTY              <= 1'b0;
   end
   else begin
      if ( SFITX_ERROR_CLEAR==1'b1 )  begin
         MPI_CORE2CORE_FULL           <= 1'b0;
         MPI_SFI2CORE_FULL            <= 1'b0;
         MPI_CORE2CORE_EMPTY          <= 1'b0;
         MPI_SFI2CORE_EMPTY           <= 1'b0;
      end
      else begin
         MPI_CORE2CORE_FULL           <= MPI_CORE2CORE_FULL   | CORERX_CORE2CORE_FULL;
         MPI_SFI2CORE_FULL            <= MPI_SFI2CORE_FULL   | CORERX_SFI2CORE_FULL;
         MPI_CORE2CORE_EMPTY          <= MPI_CORE2CORE_EMPTY | CORERX_ORE2CORE_EMPTY;
         MPI_SFI2CORE_EMPTY           <= MPI_SFI2CORE_EMPTY  | SFITX_CORE2SFI_EMPTY;
      end
   end
end


assign  MPI_FIFO_FULL[2]  =MPI_CORE2CORE_FULL;
assign  MPI_FIFO_FULL[3]  =MPI_SFI2CORE_FULL;
assign  MPI_FIFO_EMPTY[2] =MPI_CORE2CORE_EMPTY;
assign  MPI_FIFO_EMPTY[3] =MPI_SFI2CORE_EMPTY;




endmodule 


